<template>
	<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass||''" :style="initStyle" @click="onClick">
		<slot></slot>
	</view>
</template>

<script>
import actions from './actions'

export default{
	name: 'viTransition',
	props:{
		show:{
			type:[Boolean,null],
			default:false
		},
		mode:{
			type:[String,null],
			default:""
		},
		//强制动画完成时间，等比例加速或减速让动画在此时间完成
		//如果进入动画和退出动画不对称，将会导致动画速度不相等
		forceDuration:{
			type:[Number,null],
			default:null
		},
		timingFunction:{
			type:[String,null],
			default:""
		},
		delay:{
			type:[Number,null],
			default:0
		},
		styles:{
			type:[Object,String,null],
			default:""
		},
		customClass:{
			type:[String,null],
			default:""
		},
	},
	data() {
		return {
			transStyle: "", //初始样式，由事件改变
			animation: null,
			animationData: {}, //动画实例对象
			initStyle:"",
			isShow: false,
			openTimer: null,
			closeTimer: null,
		}
	},
	computed: {
		setting(){
			return {
				mode: this.mode ||"fade",
				forceDuration: this.$api.isValue(this.forceDuration)? this.forceDuration :-1,
			}
		},
		transAction(){
			let mode = this.setting.mode;
			return actions[mode]? actions[mode] : actions.fade
		},
		transOption(){
			let defSetting = {},
				customSetting = {};
				
			if(this.transAction.duration){
				defSetting["duration"] = this.transAction.duration;
			}
			if(this.transAction.timingFunction){
				defSetting["timingFunction"] = this.transAction.timingFunction;
			}
			if(this.transAction.delay){
				defSetting["delay"] = this.transAction.delay;
			}
			if(this.transAction.transformOrigin){
				defSetting["transformOrigin"] = this.transAction.transformOrigin;
			}
			
			if(this.timingFunction){
				customSetting["timingFunction"] = this.timingFunction;
			}
			if(this.delay){
				customSetting["delay"] = this.delay;
			}
			return Object.assign({}, defSetting, customSetting)
		},
		//初始化样式
		startTrans(){
			let initTransObj = this.transAction.initStyle,
				strOpacity = "",
				strTrans = "transform:";
			for(let key of Object.keys(initTransObj)){
				if(key == "opacity"){
					strOpacity += `opacity:${initTransObj[key]};`
				}else{
					strTrans += `${key}(${initTransObj[key]}) `
				}
			}
			strTrans += ";";
			return strOpacity + strTrans
		},
		//初始样式
		startStyle(){
			return this.$api.isObject(this.styles)? this.$api.objToStr(this.styles) : this.styles||""
		},
	},
	methods: {
		onClick(){
			this.$emit('click', this.isShow)
		},
		//计算动画倍速
		computeRuntime(transList){
			let runtime = 0;
			for(let tep of transList){
				if(tep.duration >0){
					runtime += tep.duration;
				}else{
					runtime += this.transAction.duration;
				}
			}
			return runtime
		},
		//创建动画
		run(open=false){
			let transList = open? this.transAction.in : this.transAction.out;
			let forceDuration = this.setting.forceDuration;
			let runtime = this.computeRuntime(transList),
				speed = forceDuration >-1? forceDuration / runtime :1;
			for(let tep of transList){
				let tepOption = {};
				if(tep.duration){
					tepOption["duration"] = parseInt(tep.duration * speed)
				}else{
					tepOption["duration"] = parseInt(this.transAction.duration * speed)
				}
				if(tep.timingFunction){
					tepOption["timingFunction"] = tep.timingFunction
				}
				if(tep.delay){
					tepOption["delay"] = tep.delay
				}
				for(let key of Object.keys(tep)){
					if(key != "duration" && key != "timingFunction" && key != "delay"){
						this.animation[key](tep[key])
					}
				}
				this.animation.step(tepOption);
			}
			this.animationData = this.animation.export();
			return forceDuration >-1? forceDuration : runtime;
		},
		// 开始过度动画
		open(){
			if(this.closeTimer){
				clearTimeout(this.closeTimer);
				this.closeTimer = null;
			}
			this.isShow = true;
			this.initStyle = "opacity:0;" + this.startStyle;
			this.$emit('change', this.isShow);
			this.$nextTick(()=>{
				//为解决movable-area因初始变换造成可拖动区域识别错误的问题，多套一层延时器
				setTimeout(()=>{
					this.initStyle = this.startTrans + this.startStyle;
					//存在打开动画立即完成的问题，用延时回避
					setTimeout(()=>{
						this.animation = uni.createAnimation(this.transOption);
						let waitTime = this.run(true);
						if(this.openTimer){
							clearTimeout(this.openTimer)
						}
						this.openTimer = setTimeout(()=>{
							this.initStyle = this.startStyle;
							this.$emit('done', this.isShow);
						}, waitTime)
					}, 20)
				},20)
			})
		},
		//关闭
		close(){
			if(!this.isShow){ return }
			let waitTime = this.run();
			this.closeTimer = setTimeout(()=>{
				this.isShow = false;
				this.animationData = null;//可清理也可不清理
				this.animation = null;//可清理也可不清理
				this.closeTimer = null;//可清理也可不清理
				this.$emit('done', this.isShow);
				this.$emit('change', this.isShow);
			}, waitTime)
		},
		
	},
	watch:{
		"show":{
			handler(newVal){
				if(newVal){
					this.open()
				}else if(this.isShow){
					this.close()
				}
			},
		}
	},
	mounted(){
		if(this.show){
			this.open()
		}
	}
}
</script>

<style></style>
